package cn.tonyandmoney.voicenotes.notes.model;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Log;

import com.dhh.rxlifecycle2.RxLifecycle;
import com.github.promeg.pinyinhelper.Pinyin;

import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cn.tonyandmoney.baidu.asr.entity.NluResult;
import cn.tonyandmoney.baidu.asr.event.AsrAudioEvent;
import cn.tonyandmoney.baidu.asr.event.AsrResultEvent;
import cn.tonyandmoney.baidu.asr.model.AsrFilter;
import cn.tonyandmoney.baidu.asr.model.AsrFilterInvocation;
import cn.tonyandmoney.baidu.asr.recognizer.RecogResult;
import cn.tonyandmoney.baidu.bus.SpeechBus;
import cn.tonyandmoney.baidu.consumers.RxAbstractConsumer;
import cn.tonyandmoney.voicenotes.R;
import cn.tonyandmoney.voicenotes.notes.bean.ReaderState;
import cn.tonyandmoney.voicenotes.notes.entity.TRecord;
import cn.tonyandmoney.voicenotes.notes.enums.OperationEnum;
import cn.tonyandmoney.voicenotes.notes.filters.OperationFilter;
import cn.tonyandmoney.voicenotes.notes.presenter.IOperation;
import cn.tonyandmoney.voicenotes.notes.presenter.IReaderInterface;
import cn.tonyandmoney.voicenotes.utils.DateUtils;
import cn.tonyandmoney.voicenotes.wav.utils.CutUtils;
import io.reactivex.schedulers.Schedulers;

/**
 * 实现该接口
 * 处理自己的事件处理，
 * 改一下逻辑
 */
public class OperationCallback extends RxAbstractConsumer<AsrResultEvent> implements IOperation {
    private final String TAG = "OperationCallback";
    private AsrFilterInvocation mInvocation = new AsrFilterInvocation();
    private int maxLength;
    protected IReaderInterface mView;
    protected ReaderState mState = new ReaderState();
    private final Object lock = new Object();
    private RecordModel recordModel;
    private OperationFilter mCommandFilter = new OperationFilter(this);

    @SuppressLint("CheckResult")
    public OperationCallback(IReaderInterface mView, Context context) {
        this.mView = mView;
        String length = PreferenceManager.getDefaultSharedPreferences(context)
                .getString(context.getString(R.string.record_max_length), "5");
        maxLength = Integer.parseInt(length);
        SpeechBus.Companion.register(AsrResultEvent.class)
                .compose(RxLifecycle.with(context).<AsrResultEvent>bindOnDestroy())
                .subscribe(this);
        SpeechBus.Companion.register(AsrAudioEvent.class)
                .compose(RxLifecycle.with(context).<AsrAudioEvent>bindOnDestroy())
                .subscribe(new RxAbstractConsumer<AsrAudioEvent>() {
                    @Override
                    public void onEvent(AsrAudioEvent event) {
                        onAudioData(event.getData(), event.getOffset(), event.getLength());
                    }
                });
    }


    @Override
    public void onEvent(AsrResultEvent event) {
        Log.i(TAG, "event: " + event);
        if (event.getCode() == AsrResultEvent.CODE_FINAL)
            onAsrFinalResult(event.getCode(), event.getResults(), event.getResult(), event.getNluResult());
    }


    /**
     * 临时识别结果，检测是否有语义识别结果，如果有，则进行处理
     * 如果当前操作为none，则不需要处理
     *
     * @param results     结果集
     * @param recogResult 识别结果
     */
    private void onAsrFinalResult(int code, String[] results, @NotNull RecogResult recogResult, final NluResult result) {
        Log.i(TAG, "onAsrFinalResult: " + recogResult);
        if (code != AsrResultEvent.CODE_FINAL) return;

        /*如果当前为无状态，则应该监听的是命令*/
        if (mState.isOperation(OperationEnum.NONE)) {
            if (result.hasNluResult()) {
                Schedulers.computation().scheduleDirect(new Runnable() {
                    @Override
                    public void run() {
                        mInvocation.doFilter(result);
                    }
                });
            }
        } else if (result.hasNluResult() && mCommandFilter.doFilter(result)) {
            /*否则，应该首先判断是否有命令语义，不响应操作语义*/
            return;
        } else {
            /*最终，是语义的输入*/
            String best = recogResult.getBestResult();
            if (TextUtils.isEmpty(best)) return;

            if (mState.isOperation(OperationEnum.INPUT_CONTENT)) {
                mView.onPartialContent(recogResult.getBestResult());
            } else if (mState.isOperation(OperationEnum.INPUT_NAME)) {
                mView.onName(recogResult.getBestResult());
            } else if (mState.isOperation(OperationEnum.INPUT_MEMO)) {
                mView.onMemo(recogResult.getBestResult());
            } else if (mState.isOperation(OperationEnum.INPUT_TYPE)) {
                mView.onType(recogResult.getBestResult());
            } else {
                //未实现
                Log.i(TAG, "未实现类型");
            }
        }
        Log.i(TAG, "完成输入。");
    }


    /**
     * 自己实现保存录音文件,只有输入内容的时候需要保存音频文件
     * 其他输入无需自己保存音频文件
     *
     * @param data   音频数据
     * @param length 长度
     * @param offset 偏移
     */
    private void onAudioData(byte[] data, int offset, int length) {
        if (mState.isOperation(OperationEnum.INPUT_CONTENT)) {
            if (recordModel == null) {
                startRecord(mView.getInputText(R.id.record_name));
            }
            recordModel.write(data);
        }
    }


    /**
     * 重新开始录音，从一进来开始就录音吧，然后到保存结束
     * 不管是标题还是命令词汇还是别的东西
     * 开始录音，开始不停止之前的录音
     */
    protected void startRecord(String name) {
        Log.i(TAG, "startRecord: " + name);
        if (recordModel == null || recordModel.isDisposed()) {
            String filename = Pinyin.toPinyin(name, "") + DateUtils.INSTANCE.toYYYYMMDDHHmmss() + ".pcm";
            try {
                recordModel = new RecordModel(filename, lock);
                recordModel.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            Log.i(TAG, "has start record");
        }
    }

    /**
     * 保存原始文件，并且按照设置的文件长度进行分割
     * 暂时不做处理,结束一次录音
     *
     * @return 文件的路径
     */
    protected List<TRecord> saveRecord(String name) {
        Log.i(TAG, "开始保存语音文件->" + name);
        if (recordModel == null) return new ArrayList<>();
        synchronized (lock) {
            try {
                if (!recordModel.isDisposed()) {
                    recordModel.dispose();
                    Log.i(TAG, "lock");
                    lock.wait();
                }
            } catch (Exception e) {
                e.printStackTrace();
                SystemClock.sleep(1000);
            }
        }
        File file = recordModel.getFile();
        Log.i(TAG, "saveRecord->" + file.getAbsolutePath());
        TRecord record = new TRecord();
        record.setContent(name);
        record.setPath(file.getAbsolutePath());
        return CutUtils.curPCM(record, maxLength);
    }


    /**
     * 添加过滤器
     *
     * @param filter 过滤器
     */
    public void addFilter(AsrFilter filter) {
        mInvocation.addFilter(filter);
    }

    @Override
    public void operation(OperationEnum operation) {
        operation(operation, new HashMap<String, String>());
    }

    @Override
    public void operation(OperationEnum operation, Map<String, String> params) {

    }
}
